﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;

namespace Gmantis.Controls
{
    public interface IOrderedSet<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {
        // Methods
        void AddRange(T from, T to);
        bool RemoveRange(T from, T to);

        // Properties
        T this[int index] { get; }
    }

    public class OrderedSet : IOrderedSet<int>, IEnumerable<int>, IEnumerable
    {
        // Fields
        private List<Range<int>> ranges;

        // Methods
        public OrderedSet()
        {
            this.ranges = new List<Range<int>>();
        }

        public OrderedSet(OrderedSet set)
            : this()
        {
            foreach (Range<int> range in set.ranges)
            {
                this.ranges.Add(new Range<int>(range.LowerBound, range.UpperBound));
            }
        }

        public void Add(OrderedSet set)
        {
            foreach (Range<int> range in set.ranges)
            {
                this.Add(range);
            }
        }

        internal void Add(Range<int> range)
        {
            this.ranges.Add(range);
            this.Coalesce();
            this.JoinRanges();
        }

        public void Add(int item)
        {
            this.Add(new Range<int>(item, item));
        }

        public void AddRange(int from, int to)
        {
            this.ranges.Add((from < to) ? new Range<int>(from, to) : new Range<int>(to, from));
            this.JoinRanges();
        }

        public void Clear()
        {
            this.ranges.Clear();
        }

        private void Coalesce()
        {
            Range<int> item = null;
            this.ranges.Sort();
            foreach (Range<int> range2 in this.ranges.ToArray())
            {
                if (item == null)
                {
                    item = range2;
                }
                else if (item.IsContiguousWith(range2))
                {
                    Range<int> range3 = item | range2;
                    int index = this.ranges.IndexOf(item);
                    this.ranges.Remove(item);
                    this.ranges.Remove(range2);
                    this.ranges.Insert(index, range3);
                    item = range3;
                }
                else
                {
                    item = range2;
                }
            }
        }

        public bool Contains(int item)
        {
            foreach (Range<int> range in this.ranges)
            {
                if ((item.CompareTo(range.LowerBound) >= 0) && (item.CompareTo(range.UpperBound) <= 0))
                {
                    return true;
                }
            }
            return false;
        }

        public void CopyTo(int[] array, int arrayIndex)
        {
            int num = arrayIndex;
            foreach (int num2 in this)
            {
                if (num < array.Length)
                {
                    array[num++] = num2;
                }
            }
        }

        public IEnumerator<int> GetEnumerator()
        {
            return new OrderedSetEnumerator(this);
        }

        private static bool IsJoinableWith(Range<int> range1, Range<int> range2)
        {
            if ((range1.UpperBound + 1) != range2.LowerBound)
            {
                return ((range2.UpperBound + 1) == range1.LowerBound);
            }
            return true;
        }

        private static Range<int> Join(Range<int> range1, Range<int> range2)
        {
            if ((range1.UpperBound + 1) == range2.LowerBound)
            {
                return new Range<int>(range1.LowerBound, range2.UpperBound);
            }
            return new Range<int>(range2.LowerBound, range1.UpperBound);
        }

        private void JoinRanges()
        {
            for (int i = 0; i < this.ranges.Count; i++)
            {
                Range<int> range = this.ranges[i];
                int num2 = i + 1;
                while (num2 < this.ranges.Count)
                {
                    Range<int> range2 = this.ranges[num2];
                    if (IsJoinableWith(range, range2))
                    {
                        int index = this.ranges.IndexOf(range);
                        int num4 = this.ranges.IndexOf(range2);
                        Range<int> item = Join(range, range2);
                        this.ranges.RemoveAt(num4);
                        this.ranges.RemoveAt(index);
                        this.ranges.Insert(index, item);
                        range = item;
                    }
                    else
                    {
                        num2++;
                    }
                }
            }
            this.Coalesce();
        }

        public bool Remove(OrderedSet removedItems)
        {
            if (removedItems.ranges.Count > 0)
            {
                foreach (Range<int> range in removedItems.ranges)
                {
                    this.Remove(range);
                }
            }
            return true;
        }

        public bool Remove(int item)
        {
            return this.Remove(new Range<int>(item, item));
        }

        internal bool Remove(Range<int> addedRange)
        {
            int index = 0;
            while (index < this.ranges.Count)
            {
                Range<int> range = this.ranges[index];
                Range<int>[] rangeArray = range.Substract(addedRange, number => number + 1, number => number - 1).ToArray<Range<int>>();
                this.ranges.RemoveAt(index);
                foreach (Range<int> range2 in rangeArray)
                {
                    this.ranges.Insert(index, range2);
                    index++;
                }
            }
            return true;
        }

        public bool RemoveRange(int from, int to)
        {
            return this.Remove(new Range<int>(from, to));
        }

        internal void Shift(int from, int to)
        {
            Range<int> range = new Range<int>(from, to);
            int index = 0;
            while (index < this.ranges.Count)
            {
                Range<int> range2 = this.ranges[index];
                if (range2.UpperBound >= range.LowerBound)
                {
                    Range<int>[] rangeArray;
                    this.ranges.RemoveAt(index);
                    if (range2.LowerBound < range.LowerBound)
                    {
                        rangeArray = new Range<int>[] { new Range<int>(range2.LowerBound, range.LowerBound - 1), new Range<int>(range.UpperBound + 1, (range.UpperBound + 1) + (range2.UpperBound - range.LowerBound)) };
                    }
                    else
                    {
                        rangeArray = new Range<int>[] { new Range<int>(range2.LowerBound + range.Length, range2.UpperBound + range.Length) };
                    }
                    foreach (Range<int> range3 in rangeArray)
                    {
                        this.ranges.Insert(index, range3);
                        index++;
                    }
                }
                else
                {
                    index++;
                }
            }
        }

        internal void Shrink(int from, int to)
        {
            Range<int> range = new Range<int>(from, to);
            int index = 0;
            while (index < this.ranges.Count)
            {
                Range<int> range2 = this.ranges[index];
                if (range2.UpperBound >= range.LowerBound)
                {
                    Range<int>[] rangeArray;
                    this.ranges.RemoveAt(index);
                    if (range2.LowerBound < range.LowerBound)
                    {
                        if (range2.UpperBound <= range.LowerBound)
                        {
                            rangeArray = new Range<int>[] { new Range<int>(range2.LowerBound, range.LowerBound - 1) };
                        }
                        else
                        {
                            rangeArray = new Range<int>[] { new Range<int>(range2.LowerBound, range2.UpperBound - range.Length) };
                        }
                    }
                    else if (range2.LowerBound <= range.UpperBound)
                    {
                        if (range2.UpperBound <= range.UpperBound)
                        {
                            rangeArray = new Range<int>[0];
                        }
                        else
                        {
                            rangeArray = new Range<int>[] { new Range<int>(range.LowerBound, range.LowerBound + ((range2.UpperBound - range.UpperBound) - 1)) };
                        }
                    }
                    else
                    {
                        rangeArray = new Range<int>[] { new Range<int>(range2.LowerBound - range.Length, range2.UpperBound - range.Length) };
                    }
                    foreach (Range<int> range3 in rangeArray)
                    {
                        this.ranges.Insert(index, range3);
                        index++;
                    }
                }
                else
                {
                    index++;
                }
            }
            this.JoinRanges();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public override string ToString()
        {
            string str = "[";
            int num = 0;
            foreach (int num2 in this)
            {
                if (num > 0)
                {
                    str = str + ",";
                }
                if (num > 10)
                {
                    str = str + "...";
                    break;
                }
                str = str + num2.ToString();
                num++;
            }
            return (str + "]");
        }

        // Properties
        public int Count
        {
            get
            {
                int num = 0;
                foreach (Range<int> range in this.ranges)
                {
                    num += (range.UpperBound - range.LowerBound) + 1;
                }
                return num;
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public int this[int index]
        {
            get
            {
                int num = 0;
                foreach (Range<int> range in this.ranges)
                {
                    int length = range.Length;
                    if ((num + length) > index)
                    {
                        return ((index - num) + range.LowerBound);
                    }
                    num += length;
                }
                return -1;
            }
        }
    }
}
